﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fireball.Syntax.Parser;
using Fireball.Syntax;

namespace Fireball.SyntaxParsers
{
    public class Parser2
    {
        private Language _Language = null;
        //private List<Pattern> _PatternLookup = new List<Pattern>();
        private Dictionary<string, List<Pattern>> _SimplesPatterns = new Dictionary<string, List<Pattern>>();
        private List<Pattern> _ComplexPatterns = new List<Pattern>();




        public Parser2(Language language)
        {
            _Language = language;
        }

        public Language Language
        {
            get
            {
                return _Language;
            }
        }

        public void Prepare()
        {
            foreach (PatternGroup g in this.Language.PatternGroups)
            {
                if (!string.IsNullOrEmpty(g.PatternsString))
                {
                    string[] keys = g.PatternsString.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);

                    foreach (string key in keys)
                    {
                        Pattern pattern = new Pattern()
                        {
                            IsComplex = false,
                            PatternGroup = g,
                            Text = key
                        };

                        InsertPatternOnLookup(pattern);
                    }


                } 
                foreach (Pattern pattern in g.Patterns)
                {
                    InsertPatternOnLookup(pattern);
                }
            }
        }

        private void InsertPatternOnLookup(Pattern pattern)
        {
            if (pattern.IsComplex)
            {
                _ComplexPatterns.Add(pattern);
            }
            else
            {
                string oneChar = pattern.Text.Substring(0, 1);

                if (!_SimplesPatterns.ContainsKey(oneChar))
                    _SimplesPatterns[oneChar] = new List<Pattern>();


                _SimplesPatterns[oneChar].Add(pattern);

                if (pattern.Text.Length < 2)
                    return;

                // inserisco il pattern con corrispondenza a due caratteri
                string twoChar = pattern.Text.Substring(0, 2);

                if (!_SimplesPatterns.ContainsKey(twoChar))
                    _SimplesPatterns[twoChar] = new List<Pattern>();

                _SimplesPatterns[twoChar].Add(pattern);

                if (pattern.Text.Length < 3)
                    return;

                string treeChar = pattern.Text.Substring(0, 3);

                if (!_SimplesPatterns.ContainsKey(treeChar))
                    _SimplesPatterns[treeChar] = new List<Pattern>();

                _SimplesPatterns[treeChar].Add(pattern);
            }
        }

            /*
            _PatternLookup.Clear();

            foreach (PatternGroup g in this.Language.PatternGroups)
            {
                foreach (Pattern p in g.Patterns)
                {
                    p.Prepare();
                    _PatternLookup.Add(p);
                }
            }
            */
       

        public SyntaxDocument ParserAll(SyntaxDocument syntaxDocument)
        {

             string text = syntaxDocument.Text;

            ParserAll(text, ref syntaxDocument);

            return syntaxDocument;
        }

        private void ParserAll(string text, ref  SyntaxDocument synDoc)
        {
            // text = text.Replace("\r", "");
           // string[] lines = text.Split(new string[] { "\n" }, StringSplitOptions.None);

            //synDoc.Lines.Clear();//Rimuovo tutte le linee

            foreach (Line currentLine in synDoc.Lines)
            {
               // Line newLine = new Line();
                currentLine.Clear();
               // newLine.Text = currentLine;

                ProcessLine(currentLine);

              //  synDoc.Lines.Add(newLine);
            }
        }

        //private void ProcessLine(Line line)
        //{
        //    PatternResult next = GetNextPatternResult(0, line.Text);
        //    StringBuilder sbWord = new StringBuilder();

        //    for (int i = 0; i < line.Text.Length; i++)
        //    {
        //        char currentChar = line.Text[i];
        //    recheck:
        //        if (next != null)
        //        {
        //            if (i >= next.StartIndex && i <= next.EndIndex)
        //            {
        //                CloseWord(ref sbWord, ref line);
        //                line.Add(new Word
        //                {
        //                    IsKeyword = true,
        //                    Text = next.Value,
        //                    Style = next.Pattern.PatternGroup.Style
        //                });
        //                i = next.EndIndex;
        //                continue;
        //            }
        //            else if (i > next.EndIndex)
        //            {
        //                next = GetNextPatternResult(i, line.Text);
        //                if (next != null)
        //                    goto recheck;
        //            }
        //        }

        //        BracketType bracketType = BracketType.None;

        //        if (currentChar == ' ')
        //        {
        //            CloseWord(ref sbWord, ref line);
        //            line.Add(new Space());
        //        }
        //        else if (currentChar == '\t')
        //        {
        //            CloseWord(ref sbWord, ref line);
        //            line.Add(new Tab());
        //        }
        //        else if (this.Language.Brackets.IsBracket(currentChar, out bracketType))
        //        {
        //            CloseWord(ref sbWord, ref line);//chiudo la word prima di procedere
        //            //all aggiunta del bracket

        //            line.Add(new Word
        //            {
        //                Text = currentChar.ToString(),
        //                IsBracket = true,
        //                BracketType = bracketType
        //            });
        //        }
        //        else
        //            sbWord.Append(currentChar);
        //    }
        //    CloseWord(ref sbWord, ref line);
        //}

        private void ProcessLine(Line line)
        {
            PatternResult next = GetNextPatternResult(0, line.Text);
            StringBuilder sbWord = new StringBuilder();

            for (int i = 0; i < line.Text.Length; i++)
            {
                char currentChar = line.Text[i];
            recheck:
                if (next != null)
                {
                    if (i >= next.StartIndex && i < next.EndIndex)
                    {
                        CloseWord(ref sbWord, ref line);
                        line.Add(new Word
                        {
                            IsKeyword = true,
                            Text = next.Value,
                            Style = next.Pattern.PatternGroup.Style
                        });
                        i = next.EndIndex-1;
                        continue;
                    }
                    else if (i > next.EndIndex)
                    {
                        next = GetNextPatternResult(i, line.Text);
                        if (next != null)
                            goto recheck;
                    }
                }

                BracketType bracketType = BracketType.None;

                if (currentChar == ' ')
                {
                    CloseWord(ref sbWord, ref line);
                    line.Add(new Space());
                }
                else if (currentChar == '\t')
                {
                    CloseWord(ref sbWord, ref line);
                    line.Add(new Tab());
                }
                else if (this.Language.Brackets.IsBracket(currentChar, out bracketType))
                {
                    CloseWord(ref sbWord, ref line);//chiudo la word prima di procedere
                    //all aggiunta del bracket

                    line.Add(new Word
                    {
                        Text = currentChar.ToString(),
                        IsBracket = true,
                        BracketType = bracketType
                    });
                }
                else
                {
                    var patterns = GetPatterns(line.Text, i, next);
                    PatternFindResult pattRes = null;
                    if (patterns != null)
                    {
                        foreach (Pattern current in patterns)
                        {
                            pattRes = current.FindIn(line.Text, i);

                            if (pattRes != null)
                                break;
                        }
                    }
                    if (pattRes == null)
                        sbWord.Append(currentChar);
                    else
                    {
                        CloseWord(ref sbWord, ref line);
                        line.Add(new Word
                        {
                            Text = pattRes.Text.ToString(),
                            IsBracket = false,
                            BracketType = BracketType.None,
                            IsKeyword = true,
                            Style = pattRes.Pattern.PatternGroup.Style
                        });
                        i = pattRes.EndPos - 1;
                    }
                }
            }
            CloseWord(ref sbWord, ref line);
        }

        /// <summary>
        /// Ottiene la collezione di pattern semplici per la linea specificata, partendo
        /// dal indice specificato e cercando una pattern collection cercando con i primi 1,
        /// 2,3 caratteri disponibili dal punto di partenza specificato
        /// </summary>
        /// <param name="line"></param>
        /// <param name="index"></param>
        /// <param name="currentComplex"></param>
        /// <returns></returns>
        private List<Pattern> GetPatterns(string line, int index, PatternResult currentComplex)
        {
            int limit = line.Length;
            if (currentComplex != null) limit = currentComplex.StartIndex;

            //Impossibile estrarre anche un solo carattere
            if ((line.Length - index) == 0) return null;
            int cChars = 1;

            if ((line.Length - index) >= 3)
                cChars = 3;
            else if ((line.Length - index) >= 2)
                cChars = 2;

            string gChar = line.Substring(index, cChars);

            List<Pattern> coll = null;

           /* while (true)
            {
                if (_SimplesPatterns.ContainsKey(gChar))
                    coll = _SimplesPatterns[gChar];

                if (coll != null)
                    break;

                if (cChars > 1)
                {
                    cChars--;
                    gChar = line.Substring(index, cChars);
                }
                else
                {
                    break;
                }
            }*/

            gChar = gChar.Trim();

            if (_SimplesPatterns.ContainsKey(gChar))
                coll = _SimplesPatterns[gChar];


            return coll;
        }

        private void CloseWord(ref StringBuilder sbWord,ref Line line)
        {
            if (sbWord.Length > 0)
            {
                line.Add(new Word { Text = sbWord.ToString() });
                sbWord = new StringBuilder();
            }
        }


        /// <summary>
        /// Ritorna il pattern result 
        /// </summary>
        /// <param name="start"></param>
        /// <param name="line"></param>
        /// <returns></returns>
        private PatternResult GetNextPatternResult(int start, string line)
        {
            PatternResult currentResult = null;

            /*foreach (PatternGroup current in this.Language.PatternGroups)
            {
                foreach (Pattern p in current.Patterns)
                {*/
            foreach (Pattern p in this._ComplexPatterns)
            {
                var pr = p.FindIn(line, start);
                if (!(pr.Equals(PatternFindResult.Empty)))
                {
                    if (currentResult != null)
                    {
                        if (currentResult.StartIndex < pr.StartPos)
                            continue;
                    }
                    currentResult = new PatternResult
                    {
                        EndIndex = pr.EndPos,
                        Pattern = p,
                        StartIndex = pr.StartPos,
                        Value = pr.Text
                    };
                    if (currentResult.StartIndex == start)
                        return currentResult;
                }
            }
            //}

            return currentResult;
        }


        public class PatternResult
        {
            public int StartIndex { get; set; }
            public int EndIndex { get; set; }
            public Pattern Pattern { get; set; }
            public string Value { get; set; }
        }

    }
}
